﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SOWY.common;

namespace SOWY
{
    class DateProcessor
    {
        private string dateToProcess;
        private string dateTypeKeywords;
        private string startOrEnd;
        private int dayToAdd;
        private DateTime comparator;

        #region constructor
        public DateProcessor()
        {
            dateToProcess = "";
            dateTypeKeywords = "";
            startOrEnd = "";
            dayToAdd = 0;
        }
        public DateProcessor(string input, string dateType)
        {
            dateToProcess = input;
            dateTypeKeywords = dateType;
            startOrEnd = "";
        }
        public DateProcessor(string input, string dateType, string startEnd)
        {
            dateToProcess = input;
            dateTypeKeywords = dateType;
            startOrEnd = startEnd;
        }
        public DateProcessor(string input, string dateType, string startEnd, DateTime compare)
        {
            dateToProcess = input;
            dateTypeKeywords = dateType;
            startOrEnd = startEnd;
            comparator = compare;
        }
        #endregion

        public DateTime processDate()
        {
            DateTime formattedDate;
            if (DateTime.TryParse(dateToProcess, out formattedDate)) //convert succeed
            {
                return formattedDate;
            }
            else
            {
                DateFormatter df = new DateFormatter(dateToProcess);
                dateToProcess = df.formatDate();
                
                string[] workTokens = dateToProcess.Split(GeneralConstant.DELIMITER_SPACE);

                formattedDate = processDateManual(workTokens);
                formattedDate = formattedDate.AddDays(dayToAdd);

                return formattedDate;
            }
        }
        private DateTime processDateManual(string[] workTokens)
        {
            DateTime formattedDate = DateTime.Now;
            switch (workTokens.Length)
            {
                case 1: formattedDate = processOneWordString(workTokens);
                    break;

                case 2://remember that need to do next week here also cause its not easy to format
                    break;

                case 3:
                    break;

                case 4:
                    break;

                case 5:
                    break;

                default:
                    break;
            }
            return formattedDate;
        }

        #region one word processing
        private DateTime processOneWordString(string[] workTokens)
        {
            //check if the string is empty, if it is, user did not entered any date and should be a floating task regardless of keyword
            if (workTokens[0].Length == 0)
            {
                this.dateTypeKeywords = "";
                return DateTime.MinValue;
                //in the event of a accidental floating task, the dates are not necessary anymore, so we just return a date that will not cause any error
            }
            else
            {
                if(checkTomorrow(workTokens[0]) || checkYesterday(workTokens[0]))
                    return DateTime.Today;
                else
                {
                    DateTime dates = DateTime.MinValue;
                    int possibleDay = checkDays(workTokens[0]);
                    int possibleMonth = checkMonths(workTokens[0]);
                    int possibleYear = checkYear(workTokens[0]);

                    if (possibleDay != 0)
                        dates = processOneWordDay(possibleDay);
                    if (possibleMonth != 0)
                        dates = processOneWordMonth(possibleMonth);
                    if (possibleYear != 0)
                        dates = processOneWordYear(possibleYear);
                
                    return dates;
                }
            }
        }

        private DateTime processOneWordYear(int input)
        {
            DateTime date = goToEndofInputYear(input);

            if (dateTypeKeywords.ToLower().CompareTo(GeneralConstant.KEY_BETWEEN) == 0)
            {
                if (startOrEnd.CompareTo("start") == 0)
                    date = goToStartofInputYear(input);
                else
                    date = goToEndofInputYear(input);
            }
            return date;
        }
        private DateTime processOneWordMonth(int input)
        {
            DateTime date = goToEndofInputMonth(input);

            if (dateTypeKeywords.ToLower().CompareTo(GeneralConstant.KEY_BETWEEN) == 0)
            {
                if (startOrEnd.CompareTo("start") == 0)
                    date = goToStartOfInputMonth(input);
                else
                    date = goToEndofInputMonth(input);
            }

            return date;
        }
        private DateTime processOneWordDay(int input)
        {
            DateTime now = DateTime.Today;
            int currentDay = checkDays(now.DayOfWeek.ToString());
            while (currentDay != input)
            {
                now = now.AddDays(1.0);
                currentDay = checkDays(now.DayOfWeek.ToString());
            }
            if (dateTypeKeywords.ToLower().CompareTo(GeneralConstant.KEY_BETWEEN) == 0)
            {
                if (now < comparator)
                    now = now.AddDays(7);
            }
            return now;
        }
        #endregion

        #region start/End of Year/Month Function
        private DateTime goToStartOfInputMonth(int input)
        {
            DateTime now = DateTime.Today;
            int currentMonth = now.Month;
            while (currentMonth != input)
            {
                now = now.AddMonths(1);
                currentMonth = now.Month;
            }
            DateTime newDate = new DateTime(now.Year, now.Month, 1);
            return newDate;
        }
        private DateTime goToEndofInputMonth(int input)
        {
            DateTime now = DateTime.Today;
            int currentMonth = now.Month;
            while (currentMonth != input)
            {
                now = now.AddMonths(1);
                currentMonth = now.Month;
            }
            int numOfDays = DateTime.DaysInMonth(now.Year, now.Month);
            DateTime newDate = new DateTime(now.Year, now.Month, numOfDays);
            return newDate;
        }

        private DateTime goToStartofInputYear(int input)
        {
            DateTime Now = new DateTime(input, 1, 1);
            return Now;
        }
        private DateTime goToEndofInputYear(int input)
        {
            DateTime Now = new DateTime(input, 12, 31);
            return Now;
        }
        #endregion

        #region CheckFunctions
        private bool checkTomorrow(string input)
        {
            if (input.ToLower().CompareTo("tomorrow") == 0 || input.ToLower().CompareTo("tmr") == 0)
            {
                dayToAdd++;
                return true;
            }
            return false;
        }
        private bool checkYesterday(string input)
        {
            if (input.ToLower().CompareTo("yesterday") == 0 || input.ToLower().CompareTo("ytd") == 0)
            {
                dayToAdd--;
                return true;
            }
            return false;
        }

        private int checkDays(string input)
        {
            for (int i = 0; i < GeneralConstant.COMMON_NUMBER_OF_DAYS; i++)
            {
                if (GeneralConstant.DAY_LONG_ALL[i].ToLower().Contains(input.Trim().ToLower()))
                {
                    return ++i;
                }
            }
            return 0;
        }
        private int checkMonths(string input)
        {
            for (int i = 0; i < GeneralConstant.COMMON_NUMBER_OF_MONTHS; i++)
            {
                if (GeneralConstant.MONTH_LONG_ALL[i].ToLower().Contains(input.Trim().ToLower()))
                {
                    return ++i;
                }
            }
            return 0;
        }
        private int checkYear(string input)
        {
            int year;
            if (int.TryParse(input, out year))
            {
                if (year < DateTime.Now.Year)
                    return 0;
                else
                    return year;
            }
            else
                return 0;
        }
        #endregion

    }
}
